
import { Color, Vec2, Vec3, View, color, director, find, log, sys, warn, UITransform, Node, EventTouch, view, math, macro, UIOpacity, tween, Size, Label, Tween, Vec4 } from "cc";
import { localKey, Singleton } from "../common/Common";
import * as fgui from "fairygui-cc";
import { InitFGuiRoot } from "../common/type";
import { App } from "../App";

// @ts-ignore
// let ClipBoard = require('clipboard.min.js');

// import ClipboardJS from '../common/clipboard.min.js';

import ClipboardJS from 'clipboard';
import { ModelComponent } from "cc";
import { Mesh } from "cc";
import { js } from "cc";
import { Sprite } from "cc";
import { PD } from "../player/PlayerData";

export default class UtilTool extends Singleton<UtilTool>{
    // 对象是否有效
    public isInvalid<T>(obj: T): boolean {
        if (obj === undefined || obj === null) return true;
        else if (typeof obj === 'number' && isNaN(obj)) return true;
        else if (typeof obj === 'string' && obj.length < 1) return true;
        return false;
    };

    /**
     * 字符串是否为空
     * @param str 字符串
     * @param type 去空格标识
     * @returns 
     */
    public isEmpty(str: string, type: number = 0): boolean {
        if (!str || typeof str !== 'string' || str.length < 1) {
            return true;
        }
        return this.trim(str).length < 1;
    }
    /**
     * 去掉字符串中空格
     * @param str 字符串
     * @param type 去空格标识
     * @returns 
     */
    public trim(str: string, type: number = 0): string {
        if (!str || typeof str !== 'string' || str.length < 1) {
            return '';
        }

        switch (type) {
            case 0: return str.replace(/(^\s*)|(\s*$)/g, "");
            case 1: return str.replace(/(^\s*)/g, "");;
            case 2: return str.replace(/(\s*$)/g, "");;
        }
        return str;
    };

    public getNextOrBack<T>(arrays: Array<T>, item: T, isNext: boolean = true): T {
        if (!arrays || arrays.length < 1)
            return null;

        if (item === null)
            return arrays[0];

        if (!isNext) {
            let i = arrays.length - 1;
            for (; i > 0; --i) {
                if (arrays[i] === item) break;
            }
            let index = i - 1 < 0 ? arrays.length - 1 : i - 1;
            return arrays[index % arrays.length];
        }

        let i = 0;
        for (i = 0; i < arrays.length; ++i) if (arrays[i] === item) break;
        return arrays[(i + 1) % arrays.length];
    };

    /**
     * 在保证原数组顺序的基础上乱序一个数组元素
     * @param list 
     * @returns 
     */
    public confusionArray(list: any[]): any[] {
        let tmp = list.slice(0);
        for (let i = list.length - 1; i >= 0; i--) {
            let randomIndex = Math.floor(Math.random() * (i + 1));
            let itemtAtIndex = tmp[randomIndex];
            tmp[randomIndex] = tmp[i];
            tmp[i] = itemtAtIndex;
        }

        return tmp;
    }

    /**
     * 获取 fun 的执行时间。一般用来调试，评估优化使用
     * @param fun 函数指针
     */
    public executeTime(fun: Function): number {
        if (typeof (fun) === 'function') {
            let _begin: number = (new Date()).getTime();
            fun();
            let _end: number = (new Date()).getTime();
            let _time = _end - _begin;
            console.info('Execute function--[[[\n' + fun.toString() + '\n]]] consumes time:' + _time / 1000 + 's.');
            return _time;
        }
        return 0;
    };

    // 在source前面填充N个element使之长度为len
    public prefix(source: string | number, element: string, len: number): string {
        return (Array(len).join(element) + source).slice(-len);
    }

    public startWith(raw: string, str: string): boolean {
        return (new RegExp('^' + str)).test(raw);
    }

    public endWith(raw: string, str: string): boolean {
        return (new RegExp(str + '$')).test(raw);
    }

    static subUTF8Str(str: string, len?: number): string {
        let strLength = 0;
        let strLen = 0;
        let strCut = '';
        strLen = str.length;
        for (let i = 0; i < strLen; i++) {
            let a = str.charAt(i);
            strLength++;
            if (escape(a).length > 4) {
                //中文字符的长度经编码之后大于4  
                strLength++;
            }
            strCut = strCut.concat(a);
            if (strLength >= len) {
                strCut = strCut.concat("...");
                return strCut;
            }
        }
        //如果给定字符串小于指定长度，则返回源字符串；  
        if (strLength < len) {
            return str;
        }
        return "?";
    }

    public format(...args: any[]): string {
        let as = [].slice.call(arguments),
            fmt = as.shift(), i = 0;
        if (this.isInvalid(fmt))
            return;

        if (typeof fmt !== 'string') fmt = fmt.toString();
        return fmt.replace(/%(\w)?(\d)?([dfsx])/ig, (_, a, b, c) => {
            let s = b ? new Array(b - 0 + 1).join(a || "") : "";
            if (c === "d") s += parseInt(as[i++]);
            if (c === "f") s += parseFloat(as[i++]);
            else if (c == "s") s += as[i++];
            return b ? s.slice(b * -1) : s;
        });
    };

    // public formatStr(str: string, max: number, extra: string = '...'): string {
    //     if (str && str.length <= max) {
    //         return str;
    //     }
    //     let cnt = 0, len = 0;
    //     max = max * 2;
    //     for (let i = 0; i < str.length; i++) {
    //         len += str.charCodeAt(i) > 255 ? 2 : 1;
    //         cnt += 1;
    //         if (len > max) break;
    //     }
    //     if (len <= max) return str;
    //     return str.substring(0, cnt - 2) + extra;
    // }

    static formatStr(msg: string, ...subst: any[]): string {
        return js.formatStr(msg, ...subst)
    }
    
    public formatByte(val: number): string {
        if (isNaN(val)) return '';
        if (val < 1000) return '' + val;
        let _str = '';
        let _units = ['K', 'M', 'B'];
        let _dividend = 1000;
        let curentUnit = _units[0]; //转换单位 
        let _strLen = (tempNum) => {
            let _strNum = tempNum.toString();
            let _index = _strNum.indexOf(".");
            let _newNum = _strNum;
            if (_index != -1) _newNum = _strNum.substring(0, _index);
            return _newNum.length;
        }

        for (let i = 0; i < 4; i++) {
            curentUnit = _units[i]
            if (_strLen(val) < 5)
                break;
            val = val / _dividend;
            _str = val.toFixed(2) + curentUnit;
        }

        return _str;
    };

    public dealNum(val: number, flag: string = ''): string {
        let _str = `${val}`;
        let abv = Math.abs(val);
        if (abv >= 1000) {
            _str = `${val > 0 ? '' : '-'}${(abv / 1000).toFixed(2)}${flag}`;
        }
        return _str;
    }

    public formatNum(val: number, flag: string = ','): string {
        if (val < 1e3)
            return `${val}`;

        let _str = `${val}`;
        let _arr = []
        for (let i = 0; i < _str.length; i++) {
            (i > 0 && i % 3 == 0) && _arr.push(flag);
            _arr.push(_str[_str.length - 1 - i])
        }
        _arr.reverse();

        return _arr.join('');
    }

    public formatScore(val: number): string {
        if (val < 10000) return '' + val;
        let _str = '';
        let _units = ['万', '亿', '万亿'];
        let _dividend = 10000;
        let curentUnit = _units[0]; //转换单位 
        let _strLen = (tempNum): number => {
            let _strNum = tempNum.toString();
            let _index = _strNum.indexOf(".");
            let _newNum = _strNum;
            if (_index != -1) _newNum = _strNum.substring(0, _index);
            return _newNum.length
        }

        for (var i = 0; i < 4; i++) {
            curentUnit = _units[i]
            if (_strLen(val) < 5)
                break;
            val = val / _dividend;
            _str = val.toFixed(2) + curentUnit;
        }

        return _str;
    }

    // 查找数组中重复元素及出现个数
    public findRepeat(arrays: any): any {
        if (typeof (arrays) != 'object')
            return null;

        let _res = []; // 
        arrays.sort();
        for (let i = 0; i < arrays.length;) {
            let _count = 0;
            for (let j = i; j < arrays.length; j++) {
                if (arrays[i] == arrays[j])
                    _count++;
            }
            _res.push([arrays[i], _count]);
            i += _count;
        }

        //_res 二维数维中保存了 值和值的重复数
        let _newArr = [];
        for (let i = 0; i < _res.length; i++)
            _newArr.push({ val: _res[i][0], count: _res[i][1] });

        return _newArr;
    }

    /**
     * 数字前面补0
     *
     * @param {number} num 操作数字
     * @param {number} len 位数
     * @returns
     * @memberof UtilFun
     */
    public static PrefixZero(num: number, len: number): string {
        return (Array(len).join('0') + num).slice(-len);
    }

    public getDataType(o: any): string {
        if (o === null)
            return 'Null';
        else if (o === undefined)
            return 'Undefined';
        else
            return Object.prototype.toString.call(o).slice(8, -1);
    };

    public deepCpy(source: Object): any {
        if (this.isInvalid(source))
            return null;

        let _type = this.getDataType(source);
        let _result: any = null;
        if (_type === 'Array')
            _result = [];
        else if (_type === 'Object')
            _result = {};
        else
            return source;

        for (let k in source) {
            let _tmp: any = source[k];
            _type = this.getDataType(_tmp);

            if (_type == 'Array' || _type == 'Object')
                _result[k] = arguments.callee(_tmp);//递归调用
            else
                _result[k] = _tmp;
        }
        return _result;
    };


    // 将source合并到des的尾部，返回合并后结果
    public mergeObject(des: Object, source: Object | Array<Object>): Object {
        if (this.isInvalid(source) || (Array.isArray(source) && source.length < 1)) return null;
        if (this.isInvalid(des)) des = {};

        let keys = Object.keys(source);
        for (let key of keys) {
            if (Array.isArray(source[key])) {
                des[key] = [];
                for (let it of source[key]) {
                    des[key].push(it);
                }
            } else des[key] = source[key];
        }
        return des;
    };

    // 将source从des中移除
    public removeObject(des: Object, source: Object | Array<Object>): Object {
        if (this.isInvalid(des)) return null;
        if (this.isInvalid(source) || (Array.isArray(source) && source.length < 1)) return des;

        if (Array.isArray(source)) {
            source.forEach((it: Object) => {
                if (this.isInvalid(it) || !(it instanceof Object)) return des;
                for (let key in it) {
                    if (it.hasOwnProperty(key) && des.hasOwnProperty(key))
                        delete des[key];
                }
            });
        } else {
            for (let k in source)
                des.hasOwnProperty(k) && delete des[k];
        }
    };

    // obj中val是唯一的
    public getKeyByValue<T>(obj: Object, val: T): string {
        if (this.isInvalid(obj) || !(obj instanceof Object)) return '';

        for (let key in obj) {
            if (obj.hasOwnProperty(key) && obj[key] === val)
                return key;
        }

        return '';
    };

    // 将string转换为number\boolean\time（'2019-1-1 00:00:00'=>1546272000000）三种类型值
    public str2Value(str: string): any {
        if (this.isEmpty(str)) return '';

        if (typeof str !== 'string') return str;
        // boolean
        let _tmp = str.toLowerCase();
        if (_tmp === 'true' || _tmp === 'false') return _tmp === 'true';
        // 数字
        let _reg = new RegExp("^[0-9]*$");
        if (_reg.test(str)) return parseInt(str);
        // 日期
        let _time = (new Date(str.replace(/-/g, '/'))).getTime();
        if (!isNaN(_time)) return _time;
        return str;
    };

    public obj2Json(obj: Object): any {
        if (this.isInvalid(obj)) return null;
        if (typeof obj === 'string') return JSON.parse(obj);

        let _res = {};
        for (let k in obj) {
            let val = obj[k];
            _res[k] = (typeof val === 'object' ? this.obj2Json(val) : this.str2Value(val));
        }

        return _res;
    };

    /**
     * 随机从数组中取一个元素
     * @param arr 数组
     */
    public randomArray<T>(arr: Array<T>): T {
        if (!arr || arr.length < 1)
            return null;

        return arr[Math.floor(Math.random() * arr.length)];;
    };

    /**
     * 将dec合并到src并返回合并后的数组
     * @param src 
     * @param dec 
     * @returns 
     */
    public concatArray<T>(src: T[], dec: T[]): T[] {
        !src && (src = []);
        if (!dec || dec.length < 1) return src;
        src = src.concat(dec).slice(0);
        return src;
    }

    /**
     * 是否事远程url,http https
     * @param url 
     */
    public isRemoteUrl(url: string): boolean {
        url = this.trim(url);
        return (url[4] === ':' || url[5] === ':');
    };

    // 通过url获取端口
    public getPortByUrl(url: string): number {
        if (!this.isEmpty(url)) {
            let reg = new RegExp(":\\d+");
            let result = reg.exec(url);
            if (result)
                return parseInt(result[0].slice(1));
        }
        return -1;
    };

    // 获取域名
    public getDomain(url: string): string {
        if (!this.isEmpty(url)) {
            let result = new RegExp("/[\\d\\w-_\\.]+").exec(url);
            if (result) {
                return result[0].slice(1);
            }
        }
        return '';
    };

    // 获取域名端口号
    public getDomainPort(url: string): number {
        if (!this.isEmpty(url)) {
            let result = new RegExp("/[\\d\\w-_\\.]+:\\d+").exec(url);
            if (result) {
                return parseInt(result[0].slice(1));
            }
        }
        return -1;
    };

    /**
     * 将日期字符串转化为时间
     * @param strDaste xxxx-xx-xx xx:xx:xx
     * return number
     */
    public getTimerByStr(strDate: string): number {
        let _timer: any = this.isInvalid(strDate) ? 0 : strDate;
        _timer = (new Date(strDate.replace(/-/g, '/'))).getTime();
        return _timer;
    };

    /**
     * 将时间戳格式化为yyyy-MM-dd HH:mm:ss
     * @param val 时间戳
     * @returns 
     */
    static formatTimeStamp(val: number): string {
        const date = new Date(val);
        const year = date.getFullYear();
        const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
        const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
        const hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
        const minute = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
        const second = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
        return `${year}-${month}-${day}  ${hour}:${minute}:${second}`;
    }

    /**
     * 获取时间间隔
     * @param time 开始计时的时间点 (new Date()).getTime()
     * @returns 过去了多少秒
     */
    getTimeInterval(time: number): number {
        let _now = (new Date()).getTime();
        if (isNaN(time) || time < 0 || time > _now) {
            time = _now;
            return 0;
        }
        let _tmp = time;
        time = _now;
        return parseFloat(((_now - _tmp) / 1000).toFixed(3));
    };

    /**
     * 随机一个整数 包括min和max
     */
    public random(min: number, max: number): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        let count = Math.max(max - min, 0) + 1;
        return Math.floor(Math.random() * count) + min;
    }

    public createUUID(): string {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
            let r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }


    /**
     * 复制二维数组
     * @param array 目标数组 
     */
    public copy2DArray(array: any[][]): any[][] {
        let newArray: any[][] = [];
        for (let i = 0; i < array.length; i++) {
            newArray.push(array[i].concat());
        }
        return newArray;
    }

    /**
    * Fisher-Yates Shuffle 随机置乱算法
    * @param array 目标数组
    */
    public fisherYatesShuffle(array: any[]): any[] {
        let count = array.length;
        while (count) {
            let index = Math.floor(Math.random() * count--);
            let temp = array[count];
            array[count] = array[index];
            array[index] = temp;
        }
        return array;
    }

    /**
    * 混淆数组
    * @param array 目标数组
    */
    public confound(array: []): any[] {
        let result = array.slice().sort(() => Math.random() - .5);
        return result;
    }

    /**
     * 数组扁平化
     * @param array 目标数组
     */
    public flattening(array: any[]) {
        for (; array.some(v => Array.isArray(v));) {    // 判断 array 中是否有数组
            array = [].concat.apply([], array); // 压扁数组
        }
        return array;
    }

    // /**
    // * 数组去重
    // * @param array 目标数组
    // */
    // public  removeRepeated(array: []): any[] {
    //     let newArray = [...new Set(array)];
    //     return newArray;
    // }

    /**
    * 合并数组
    * @param array1 目标数组1
    * @param array2 目标数组2
    */
    public combineArrays(array1: any[], array2: any[]): any[] {
        let newArray = [...array1, ...array2];
        return newArray;
    }

    /**
    * 获取随机数组成员
    * @param array 目标数组
    */
    public getRandomValueInArray(array: any[]): any {
        let newArray = array[Math.floor(Math.random() * array.length)];
        return newArray;
    }


    /**
     * 将普通文本编码为 Base64 格式文本
     * @param string 
     * @see
     */
    public encodeString(string: string) {
        // codes from http://www.webtoolkit.info/javascript-base64.html
        const keyString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

        let base64 = '';
        let i = 0;
        let chr1: number, chr2: number, chr3: number,
            enc1: number, enc2: number, enc3: number, enc4: number;

        string = this.encodeUtf8(string);

        while (i < string.length) {
            chr1 = string.charCodeAt(i++);
            chr2 = string.charCodeAt(i++);
            chr3 = string.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            base64 = base64 + keyString.charAt(enc1) + keyString.charAt(enc2) + keyString.charAt(enc3) + keyString.charAt(enc4);
        }

        return base64;
    }

    /**
     * 将 Base64 格式文本解码为普通文本
     * @param base64 
     */
    public decodeString(base64: string) {
        // codes from http://www.webtoolkit.info/javascript-base64.html
        const keyString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

        let string = '';
        let i = 0;
        let chr1: number, chr2: number, chr3: number,
            enc1: number, enc2: number, enc3: number, enc4: number;

        base64 = base64.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < base64.length) {
            enc1 = keyString.indexOf(base64.charAt(i++));
            enc2 = keyString.indexOf(base64.charAt(i++));
            enc3 = keyString.indexOf(base64.charAt(i++));
            enc4 = keyString.indexOf(base64.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            string = string + String.fromCharCode(chr1);

            if (enc3 != 64) {
                string = string + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                string = string + String.fromCharCode(chr3);
            }
        }

        string = this.decodeUtf8(string);

        return string;
    }

    /**
     * 将普通文本编码为 UTF-8 格式文本
     * @param string 
     */
    public encodeUtf8(string: string) {
        // codes from http://www.webtoolkit.info/javascript-base64.html
        string = string.replace(/\r\n/g, "\n");
        let utf8 = '';

        for (let i = 0; i < string.length; i++) {
            const c = string.charCodeAt(i);

            if (c < 128) {
                utf8 += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utf8 += String.fromCharCode((c >> 6) | 192);
                utf8 += String.fromCharCode((c & 63) | 128);
            } else {
                utf8 += String.fromCharCode((c >> 12) | 224);
                utf8 += String.fromCharCode(((c >> 6) & 63) | 128);
                utf8 += String.fromCharCode((c & 63) | 128);
            }
        }

        return utf8;
    }

    /**
     * 将为 UTF-8 格式文本解码为普通文本
     * @param utf8 
     */
    public decodeUtf8(utf8: string) {
        // codes from http://www.webtoolkit.info/javascript-base64.html
        let string = '';
        let i = 0;
        let c1 = 0, c2 = 0, c3 = 0;

        while (i < utf8.length) {
            c1 = utf8.charCodeAt(i);

            if (c1 < 128) {
                string += String.fromCharCode(c1);
                i++;
            } else if ((c1 > 191) && (c1 < 224)) {
                c2 = utf8.charCodeAt(i + 1);
                string += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utf8.charCodeAt(i + 1);
                c3 = utf8.charCodeAt(i + 2);
                string += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }

        return string;
    }

    /**
     * (仅 Web 平台下可用) 将 Base64 文本转为二进制数据
     * @param base64 
     */
    public base64ToBlob(base64: string) {
        if (!window || !window.atob) {
            return null;
        }
        const strings = base64.split(',');
        const type = /image\/\w+|;/.exec(strings[0])[0];
        const data = window.atob(strings[1]);
        const arrayBuffer = new ArrayBuffer(data.length);
        const uint8Array = new Uint8Array(arrayBuffer);
        for (let i = 0; i < data.length; i++) {
            uint8Array[i] = data.charCodeAt(i) & 0xff;
        }
        return new Blob([uint8Array], { type: type });
    }


    /**
     * 清除当前 URL 的参数（修改历史记录，不会刷新当前网页）
     */
    public clearUrlParam(): void {
        if (!window || !window.history) {
            return;
        }
        window.history.replaceState({}, null, '.');
    }

    /**
     * 设置当前 URL 的参数（修改历史记录，不会刷新当前网页）
     * @param param 参数
     */
    public setUrlParam(param: string | { key: string, value: string }[]): void {
        if (!window || !window.history) {
            return;
        }
        if (Array.isArray(param)) {
            param = param.map(v => `${v.key}=${v.value}`).join('&');
        }
        window.history.replaceState({}, null, `?${param}`);
    }

    /**
     * 获取当前 URL 的参数
     * @param key 键
     */
    public getUrlParam(key: string): string {
        if (!window || !window.location) {
            return null;
        }
        const query = window.location.search.replace('?', '');
        if (query === '') {
            return null;
        }
        const substrings = query.split('&');
        for (let i = 0; i < substrings.length; i++) {
            const keyValue = substrings[i].split('=');
            if (decodeURIComponent(keyValue[0]) === key) {
                return decodeURIComponent(keyValue[1]);
            }
        }
        return null;
    }

    /**
     * 获取当前 URL 的所有参数
     */
    public getUrlParams() {
        if (!window || !window.location) {
            return [];
        }
        const query = window.location.search.replace('?', '');
        if (query === '') {
            return [];
        }
        const substrings = query.split('&'),
            params: { key: string, value: string }[] = [];
        for (let i = 0; i < substrings.length; i++) {
            const keyValue = substrings[i].split('=');
            params.push({
                key: keyValue[0],
                value: keyValue[1],
            });
        }
        return params;
    }


    /**
     * 复制文本至设备剪贴板
     * @param value 文本内容
     */
    static copyText(copyWords: string,callback=null): boolean {
        // if (!document) {
        //     return false;
        // }
        // // 创建输入元素
        // const element = document.createElement('textarea');
        // element.readOnly = true;
        // element.style.opacity = '0';
        // element.value = value;
        // document.body.appendChild(element);
        // // 选择元素
        // element.select();
        // // 兼容低版本 iOS 的特殊处理
        // const range = document.createRange();
        // range.selectNodeContents(element);
        // const selection = getSelection();
        // selection.removeAllRanges();
        // selection.addRange(range);
        // // 复制
        // const result = document.execCommand('copy');
        // element.remove();
        // return result;


        if (!document) {
            return
        }

        if(App.textareaInput==null) {
            App.textareaInput = document.createElement('textarea');
            document.body.appendChild(App.textareaInput);
        }
        
        App.textareaInput.value = copyWords;
        App.textareaInput.style.width = '1px';
        App.textareaInput.style.height = '1px';
        App.textareaInput.style.left = '-9999px';

        App.textareaInput.readOnly = true;  // 这一句主要是去掉软键盘的显示
        App.textareaInput.style.opacity = '0';
        
        // 选择元素
        App.textareaInput.select();
        App.textareaInput.setSelectionRange(0, copyWords.length)    // 这句主要是在ios上可以选定复制的内容
        // 兼容低版本 iOS 的特殊处理
        // const range = document.createRange();
        // range.selectNodeContents(element);
        // const selection = getSelection();
        // selection.removeAllRanges();
        // selection.addRange(range);
        // 复制
        const result = document.execCommand('copy');
        // element.remove();
        if (callback) {
            UtilTool.printLog("success:",result)
            callback(result)
        }
    }

    // public  copyViaNavigator(value: string) {
    //     return new Promise<boolean>(res => {
    //         if (document.location.protocol === 'https:' || document.location.hostname === 'localhost' || document.location.hostname === '127.0.0.1') {
    //             // navigator.clipboard API 只适用于安全连接
    //             UtilTool.printLog('Copy via navigator.clipboard');
    //             navigator.clipboard.writeText(value)
    //                 .then(() => {
    //                     UtilTool.printLog('Clipboard write success!');
    //                     res(true);
    //                 })
    //                 .catch(err => {
    //                     console.error('Clipboard write failed:', err);
    //                     res(false);
    //                 });
    //         } else {
    //             UtilTool.printLog('Copy via document.execCommand');
    //         }
    //     });
    // }


    /**
     * 将 16 进制（hex）颜色字符串转为 RGBA 格式
     * @param {string} hex 
     * @example
     * ColorUtil.hexToRgba('#FFFFFF'); // { r: 255, g: 255, b: 255, a: 255 }
     */
    public hexToRgba(hex: string) {
        if (!this.isHex(hex)) {
            return null;
        }
        const r = parseInt(hex.substr(1, 2), 16) || 0,
            g = parseInt(hex.substr(3, 2), 16) || 0,
            b = parseInt(hex.substr(5, 2), 16) || 0,
            a = parseInt(hex.substr(7, 2), 16) || 255;
        return { r, g, b, a };
    }


    /**
     * 将 RGB 或 RGBA 颜色值转为 16 进制（hex）颜色字符串
     * @param color 
     * @example
     * const color = {
     *   r: 255,
     *   g: 255,
     *   b: 255,
     * };
     * ColorUtil.rgbaToHex(color);  // '#FFFFFF'
     */
    public rgbaToHex(color: { r: number, g: number, b: number, a?: number }) {
        const r = (color.r | 1 << 8).toString(16).slice(1),
            g = (color.g | 1 << 8).toString(16).slice(1),
            b = (color.b | 1 << 8).toString(16).slice(1);
        if (color.a == undefined) {
            return `#${r}${g}${b}`.toUpperCase();
        }
        const a = (color.a | 1 << 8).toString(16).slice(1);
        return `#${r}${g}${b}${a}`.toUpperCase();
    }

    /**
     * 测试字符串是否为 16 进制（hex）颜色值
     * @param hex 
     */
    public isHex(hex: string) {
        return /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(hex);
    }

    // /**
    //  * 将 16 进制（hex）颜色字符串转为 RGB 格式
    //  * @param {string} hex 
    //  */
    // public  hexToRGB(hex: string) {
    //     // 是否为 HEX 格式
    //     if (!ColorUtil.isHex(hex)) {
    //         return null;
    //     }
    //     // 四位
    //     if (hex.length === 4) {
    //         const r = hex.substr(1, 1),
    //             g = hex.substr(2, 1),
    //             b = hex.substr(3, 1);
    //         hex = `#${r}${r}${g}${g}${b}${b}`;
    //     }
    //     // 转换进制
    //     const rgb = {
    //         r: parseInt(`0x${hex.substr(1, 2)}`),
    //         g: parseInt(`0x${hex.substr(3, 2)}`),
    //         b: parseInt(`0x${hex.substr(5, 2)}`),
    //     };
    //     return rgb;
    // }


    /**
     * 打出光彩夺目的日志（黑蓝白配色）
     * @param title 标题
     * @param msg 信息
     */
    public log(title: any, msg?: any): void {
        if (msg) {
            UtilTool.printLog(
                `%c ${title} %c ${msg} `,
                'background: #35495E;padding: 1px;border-radius: 2px 0 0 2px;color: #fff;',
                'background: #409EFF;padding: 1px;border-radius: 0 2px 2px 0;color: #fff;'
            );
        } else {
            UtilTool.printLog(
                `%c ${title} `,
                'background: #409EFF;padding: 1px;border-radius: 0 2px 2px 0;color: #fff;',
            );
        }
    }

    /**
     * 展示动态图集
     * @param status 状态
     */
    public showDynamicAtlas(status: boolean = true): Node {
        // return dynamicAtlasManager.showDebug(status);
        return
    }

    /**
     * 展示左下角的统计面板
     * @param status 状态
     */
    public showStats(status: boolean = true): void {
        // debug.setvsStats(status);
    }

    /**
     * 更改统计面板的文本颜色
     * @param font 文本颜色
     */
    public setStatsColor(font: Color = Color.WHITE, background: Color = color(0, 0, 0, 150)) {
        const profiler = find('PROFILER-NODE');
        if (!profiler) return warn('未找到统计面板节点！');

        // 文字
        /*   profiler.children.forEach(node => node.color = font);
  
          // 背景
          let node = profiler.getChildByName('BACKGROUND');
          if (!node) {
              node = new Node('BACKGROUND');
              profiler.addChild(node, macro.MIN_ZINDEX);
              node.setContentSize(profiler.getBoundingBoxToWorld());
              node.setPosition(0, 0);
          }
          const graphics = node.getComponent(Graphics) || node.addComponent(Graphics);
          graphics.clear();
          graphics.rect(-5, 12.5, node.width + 10, node.height - 10);
          graphics.fillColor = background;
          graphics.fill(); */
    }

    /**
     * 上一次渲染帧所提交的渲染批次总数
     */
    public getDrawCalls(): number {
        // return renderer.drawCalls;
        return
    }

    /**
     * 返回手机屏幕安全区域，如果不是异形屏将默认返回设计分辨率尺寸。目前只支持安卓、iOS 原生平台和微信小游戏平台。 
     */
    public getSafeAreaRect() {
        return sys.getSafeAreaRect();
    }

    /** 当前平台 */
    public get platform() { return sys.platform; }

    /** 当前操作系统 */
    public get os() { return sys.os; }

    /** 是否为安卓手机 */
    // public  get isAndroid() { return sys.platform === sys.ANDROID; }

    /** 是否为原生环境 */
    public get isNative() { return sys.isNative; }

    /** 是否为浏览器环境 */
    public get isBrowser() { return sys.isBrowser; }

    /** 是否为手机 */
    public get isMobile() { return sys.isMobile; }

    /** 是否为苹果手机 */
    // public  get isIPhone() { return sys.platform === sys.IPHONE; }

    /** 是否为苹果平板 */
    // public  get isIPad() { return sys.platform === sys.IPAD; }

    /** 是否为手机浏览器 */
    // public  get isMobileBrowser() { return sys.platform === sys.MOBILE_BROWSER; }

    /** 是否为桌面浏览器 */
    // public  get isDesktopBrowser() { return sys.platform === sys.DESKTOP_BROWSER; }

    /** 是否为微信小游戏 */
    // public  get isWeChat() { return sys.platform === sys.WECHAT_GAME; }

    /** 是否为 QQ 小游戏 */
    // public  get isQQPlay() { return sys.platform === sys.QQ_PLAY; }

    /** 是否为字节小游戏 */
    // public  get isByteDance() { return sys.platform === sys.BYTEDANCE_GAME; }

    /** 是否为百度小游戏 */
    // public  get isBaidu() { return sys.platform === sys.BAIDU_GAME; }

    /** 是否为 vivo 小游戏 */
    // public  get isVivo() { return sys.platform === sys.VIVO_GAME; }

    /** 是否为 OPPO 小游戏 */
    // public  get isOPPO() { return sys.platform === sys.OPPO_GAME; }

    /** 是否为小米小游戏 */
    // public  get isXiaomi() { return sys.platform === sys.XIAOMI_GAME; }

    /** 是否为华为小游戏 */
    // public  get isHuawei() { return sys.platform === sys.HUAWEI_GAME; }

    /** 是否为支付宝小游戏 */
    // public  get isAlipay() { return sys.platform === sys.ALIPAY_GAME; }




    /**
     * 判断点是否在线段上
     * @param p 目标点
     * @param a 线段端点 a
     * @param b 线段端点 b
     */
    public pointOnLine(p: Vec3, a: Vec3, b: Vec3) {
        // 使用叉乘判断三点共线
        // const ab = b.sub(a);
        // const ap = p.sub(a);
        // const collinear = (ab.cross(ap).mag() === 0);
        // // 根据斜率判断三点共线
        // // const collinear = ((p.x - a.x) * (b.y - a.y) === (b.x - a.x) * (p.y - a.y));
        // // 确认点不在线段的延长线上
        // const between = (p.x >= Math.min(a.x, b.x) && p.x <= Math.max(a.x, b.x)) &&
        //     (p.y >= Math.min(a.y, b.y) && p.y <= Math.max(a.y, b.y));
        // // 满足两个条件则点在线段上
        // return collinear && between;
    }

    /**
     * 判断点是否在三角形内（同向法）
     * @param p 目标点
     * @param a 三角形顶点 a
     * @param b 三角形顶点 b
     * @param c 三角形顶点 c
     */
    public pointInTriangle(p: Vec3, a: Vec3, b: Vec3, c: Vec3) {
        function sameSide(_p: Vec3, _a: Vec3, _b: Vec3, _c: Vec3) {
            // const ab = _b.sub(_a);
            // const ac = _c.sub(_a);
            // const ap = _p.sub(_a);
            // const v1 = ab.cross(ac);
            // const v2 = ab.cross(ap);
            // return v1.dot(v2) >= 0;
        }
        // return sameSide(p, a, b, c) && sameSide(p, b, c, a) && sameSide(p, c, a, b);
        // return (a.x - p.x) * (b.y - p.y) - (b.x - p.x) * (a.y - p.y) >= 0 &&
        //     (b.x - p.x) * (c.y - p.y) - (c.x - p.x) * (b.y - p.y) >= 0 &&
        //     (c.x - p.x) * (a.y - p.y) - (a.x - p.x) * (c.y - p.y) >= 0;
    }

    /**
     * 获取点到线段的最短距离
     * @param p 目标点
     * @param a 线段端点 a
     * @param b 线段端点 b
     */
    public pointLineDistance(p: Vec3, a: Vec3, b: Vec3) {
        // 线段向量
        // const ab = b.sub(a);
        // // 斜边向量（由目标点和线段某端点组成）
        // const ap = p.sub(a);
        // // 两线段夹角（弧度制）
        // const radians = Vec3.angle(ab, ap);
        // // 斜边长度
        // const length = ap.mag();
        // // 计算距离
        // const distance = Math.sin(radians) * length;
        // // DONE
        // return distance;
    }


    /**
    * 获取一个 min 到 max 范围内的随机整数
    * @param min 最小值
    * @param max 最大值
    */
    public getRandomInt(min: number = 0, max: number = 1): number {
        return Math.floor(Math.random() * (max - min) + min);
    }

    /**
     * 获取一个伪随机整数
     * @param seed 随机种子
     * @param key key
     */
    public getPseudoRandomInt(seed: number, key: number): number {
        return Math.ceil((((seed * 9301 + 49297) % 233280) / 233280) * key);
    }

    /**
     * 获取两点间的角度
     * @param p1 点1
     * @param p2 点2
     */
    static getAngle(p1: Vec2, p2: Vec2): number {
        return Math.atan((p2.y - p1.y) / (p2.x - p1.x));
    }

    /**
     * 获取两点间的距离
     * @param p1 点1
     * @param p2 点2
     */
    static getDistance(p1: Vec2, p2: Vec2): number {
        // return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));

        let lngDiff = p1.x - p2.x; //起点与终点的x差
        let latDiff = p1.y - p2.y; //起点与终点的y差
        return Math.sqrt(lngDiff * lngDiff + latDiff * latDiff);//返回根据公式计算的结果
    }
    /**
     * 将角度转为弧度
     * @param angle 角度
     */
    static angleToRadian(angle: number): number {
        return angle * Math.PI / 180;
    }

    /**
     * 将弧度转为角度
     * @param radian 弧度
     */
    static radianToAngle(radian: number): number {
        return radian*180/Math.PI;
    }

    /**
     * 浮点数加法运算（避免浮点数加法精度问题）
     * @param a 数
     * @param b 数
     */
    public addSafely(a: number, b: number): number {
        const aDigits = (a.toString().split('.')[1] || '').length;
        const bDigits = (b.toString().split('.')[1] || '').length;
        const multiplier = Math.pow(10, Math.max(aDigits, bDigits));
        return (a * multiplier + b * multiplier) / multiplier;
    }

    /**
     * 获取两点的角度 2023年6月30日14:52:45 (已屏幕右边为0度，逆时针为正角度)
      */
    static getPointAngle(pos1, pos2){
        if (pos1.x == pos2.x && pos1.y == pos2.y) {
            return 0
        }

        let x
        let y
        let z
        let rad
        let deg

        x = pos2.x - pos1.x
        y = pos2.y - pos1.y
        z = Math.sqrt(x * x + y * y)

        rad = Math.acos(x / z)
        deg = 180 * rad / Math.PI

        if (y<0) {
            deg = -deg
        }

        return deg
    }

    /**
     * 角度的转化 2023年6月30日14:52:49
      */
    static getRAngle(angle){
        while (angle < 0){
            angle = angle + 360
        }
        return angle % 360
    }

    /**
     * 一个点按照一定的角度移动一定的距离
      */
    static getPointMove(angle, r) {
        // let rad = Math.rad(angle)
        let rad = angle*Math.PI/180

        let x2 = Math.cos(rad)*r
        let y2 = Math.sin(rad)*r

        return new Vec2(x2,y2)
    }

    /**
     * 一个点按照一定的角度移动一定的距离,返回最后那个点
      */
    static getPointMove2(startPoint:Vec2, angle, r) {
        // let rad = Math.rad(angle)
        let rad = angle*Math.PI/180

        let x2 = Math.cos(rad)*r
        let y2 = Math.sin(rad)*r

        let endPoint = new Vec2(startPoint.x + x2, startPoint.y + y2)
        return endPoint
    }

    /**
     * 一个点按照一定的角度移动一定的距离,返回最后那个点(注:这里返回的是2D对应的当地坐标)
      */
    static getPointMove3(startPoint:Vec2, angle, r) {
        // let rad = Math.rad(angle)
        let rad = angle*Math.PI/180

        let x2 = Math.sin(rad)*r
        let y2 = Math.cos(rad)*r

        let endPoint = new Vec2(startPoint.x + x2, startPoint.y + y2)
        return endPoint
    }


    // /**
    //  * 获取节点在目标节点（容器）下的相对位置
    //  * @param node 节点
    //  * @param container 目标节点（容器）
    //  */
    // public  getRelativePosition(node: Node, container: Node): Vec2 {
    //     const worldPos = (node.getParent() || node).convertToWorldSpaceAR(node.getPosition());
    //     return container.convertToNodeSpaceAR(worldPos);
    // }

    // /**
    //  * 坐标是否在目标节点范围内
    //  * @param pos 坐标
    //  * @param target 目标节点
    //  */
    // public  isPosOnNodeRect(pos: Vec2, target: Node): boolean {
    //     const rect = target.getBoundingBoxToWorld();
    //     return rect.contains(pos);
    // }

    // /**
    //  * 两个节点是否重叠
    //  * @param node1 节点 1
    //  * @param node2 节点 2
    //  * @param contains 是否完全包含
    //  */
    // public  areNodesOverlap(node1: Node, node2: Node, contains: boolean = false): boolean {
    //     const rect1 = node1.getBoundingBoxToWorld(),
    //         rect2 = node2.getBoundingBoxToWorld();
    //     return contains ? rect1.containsRect(rect2) : rect1.intersects(rect2);
    // }

    // /**
    //  * 获取节点本身在世界坐标系下的对齐轴向的包围盒（不包含子节点）
    //  * @param node 节点
    //  */
    // public  getNodeSelfBoundingBoxToWorld(node: Node) {
    //     node.parent['_updateWorldMatrix']();
    //     const { width, height } = node.getContentSize(),
    //         anchorPoint = node.getAnchorPoint(),
    //         rect = rect(
    //             -anchorPoint.x * width,
    //             -anchorPoint.y * height,
    //             width,
    //             height
    //         );
    //     node['_calculWorldMatrix']();
    //     rect.transformMat4(rect, node['_worldMatrix']);
    //     return rect;
    // }

    /**
     * 判断指定的值是否为对象
     * @param value 值
     */
    public isObject(value: any): boolean {
        return Object.prototype.toString.call(value) === '[object Object]';
    }

    /**
     * 深拷贝
     * @param target 目标
     */
    public deepCopy(target: any): any {
        if (target == null || typeof target !== 'object') {
            return target;
        }

        if (target instanceof Array) {
            const result = [];
            for (let i = 0, length = target.length; i < length; i++) {
                result[i] = this.deepCopy(target[i]);
            }
            return result;
        }

        if (target instanceof Object) {
            const result = {};
            for (const key in target) {
                if (target.hasOwnProperty(key)) {
                    result[key] = this.deepCopy(target[key]);
                }
            }
            return result;
        }

        if (target instanceof Date) {
            return (new Date()).setTime(target.getTime());
        }

        console.warn(`不支持的类型：${target}`);
        return null;
    }

    /**
     * 拷贝对象
     * @param target 目标
     */
    public copy(target: object): object {
        return JSON.parse(JSON.stringify(target));
    }

    /**
     * 判断字符是否为双字节字符（如中文字符）
     * @param string 原字符串
     */
    public isDWORD(string: string): boolean {
        return /[^\x00-\xff]/.test(string);
    }

    /**
     * 保存数据到本地
     * @param key 键
     * @param value 值
     */
    public setLocalItem(key: string, value: any) {
        if (typeof value === 'object') {
            sys.localStorage.setItem(key, JSON.stringify(value));
        } else {
            sys.localStorage.setItem(key, value);
        }
    }

    /**
     * 读取本地数据
     * @param key 键
     * @param parse 解析
     */
    public getLocalItem(key: string, def:any, parse: boolean = true) {
        const dataString = sys.localStorage.getItem(key);
        if (dataString) {
            if (parse) {
                try {
                    return JSON.parse(dataString);
                } catch {
                    return dataString;
                }
            }
            return dataString;
        }
        return def;
    }

    /**
     * 移除本地数据
     * @param key 键
     */
    public removeLocalItem(key: string) {
        sys.localStorage.removeItem(key);
    }

    public ChangeTime(time) {
        //时间总的秒数
        let seconds = time;
        //开始时间
        let second = seconds % 60;
        let startMinutes = Math.floor(seconds / 60);
        let hours = Math.floor(seconds / 3600);
        let s = second >= 10 ? second : '0' + second,
            m = startMinutes >= 10 ? startMinutes : '0' + startMinutes,
            h = hours >= 10 ? hours : '0' + hours;
        return `${h}:${m}:${s}`
    }

    //加载FGUL 文件并 加入节点
    public initFGuiRoot(resourcesName: string, packageName: string, componentName: string): Promise<InitFGuiRoot> {
        // if (App.language.languageId_default != App.language.language_ChineseId) {
        //     resourcesName = App.language.getLanguageFGUIUrl(resourcesName)
        // }

        componentName = App.language.getLanguageFGUIName(componentName)
        UtilTool.printLog(componentName,'componentName');
        return new Promise((resolve, rejcet) => {
            fgui.GRoot.create();
            fgui.UIPackage.loadPackage(resourcesName + '/' + packageName, (err,data) => {
                UtilTool.printLog(data,'data');
                let _view = fgui.UIPackage.createObject(packageName, componentName).asCom;
                _view.setSize(fgui.GRoot.inst.width, fgui.GRoot.inst.height);
                _view.makeFullScreen();
                fgui.GRoot.inst.addChild(_view);
                resolve({
                    component: _view,
                    clear() {
                        _view.dispose()
                    },
                    getCompents(componentName) {
                        UtilTool.printLog(componentName, packageName);

                        return fgui.UIPackage.createObject(packageName, componentName).asCom;
                    },
                    getItemURL(imageName) {
                        return fgui.UIPackage.getItemURL(packageName, imageName);
                    },
                    removePackage(){
                        fgui.UIPackage.removePackage(packageName);
                    }
                })
            });
        })
    }
    //预加载ui包
    static preloadFGUIPack(resourcesName: string, packageName: string){
        return new Promise<void>((resolve,rejcet) =>{
            fgui.UIPackage.loadPackage(resourcesName + '/' + packageName, (err,data) => {
                resolve();
            })
        })
    }
    static preloadScene(sceneName){
        return new Promise<void>((resolve,rejcet) =>{
            director.preloadScene(sceneName, function () {
                UtilTool.printLog('Next scene preloaded');
                resolve()
            });
        })
    }
    //格式化
    static resolveUserName = (address) => {
        let userWalletAddress = address.split('')
        return [...userWalletAddress.splice(0, 6), '...', ...userWalletAddress.splice(-6)].join('');
    }
    

    //排名对应奖励
    static calculatingRankingRewards = (num: number) => {
        UtilTool.printLog(num, 'num xxx');
        if (num <= 0) {
            return '-'
        }else if (num == 1) {
            return '300U'
        } else if (num == 2) {
            return '200U'
        } else if (num >= 3 && num <= 10) {
            return '100U'
        } else if (num >= 11 && num <= 100) {
            return '50U'
        } else if (num >= 101 && num <= 300) {
            return '10U'
        } else if (num >= 301 && num <= 500) {
            return '8U'
        } else if (num >= 501 && num <= 700) {
            return '5U'
        } else if (num >= 701 && num <= 1000) {
            return '3U'
        }
    }
    static getTime(second, getDateType) {
        var date = new Date(second);
        if (getDateType == 0) {
          return date.getFullYear();
        } else if (getDateType == 1) {
          if ((date.getMonth() + 1) <= 9) {
            return "0" + (date.getMonth() + 1);
          } else {
            return date.getMonth() + 1;
          }
        } else if (getDateType == 2) {
          if (date.getDate() <= 9) {
            return "0" + date.getDate();
          } else {
            return date.getDate();
          }
        } else if (getDateType == 3) {
          if (date.getHours() <= 9) {
            return "0" + date.getHours();
          } else {
            return date.getHours();
          }
        } else if (getDateType == 4) {
          if (date.getMinutes() <= 9) {
            return "0" + date.getMinutes();
          } else {
            return date.getMinutes();
          }
        } else if (getDateType == 5) {
          return date.getSeconds();
        } else {
          alert("输入时间格式有误!");
          return;
        }
    }

    static calculatedTimeout(){
        class Timeout {
            private timer: number | null = null;
            private resolveFunc: (() => void) | null = null;
          
            constructor(private timeoutMs: number) {}
          
            public start(): Promise<void> {
              return new Promise((resolve, reject) => {
                this.resolveFunc = resolve;
                // @ts-ignore
                this.timer = setTimeout(() => {
                  this.timer = null;
                  UtilTool.printLog('当前已超时');
                  
                  reject(new Error('Timeout'));
                }, this.timeoutMs);
              });
            }
          
            public cancel(): void {
              if (this.timer) {
                clearTimeout(this.timer);
                this.timer = null;
                if (this.resolveFunc) {
                  this.resolveFunc();
                  this.resolveFunc = null;
                }
              }
            }
          }
          const timeout = new Timeout(30000); //30秒未中断超时
          async function example(timeout: Timeout): Promise<void> {
            return new Promise(async (reslove,reject) =>{
                try {
                    Promise.race([
                      timeout.start(),
                      /* 等待的异步任务 */
                    ])
                    .then(() =>{
                        reslove()
                    })
                    .catch(() =>{
                        reject()
                    })
                    // UtilTool.printLog('异步任务完成');
                  } catch (err) {
                    console.error(err);
                  }
            })
          }
          
          // 示例用法
        //   const timeout = new Timeout(10000);
          
          // 启动异步任务
         
          
          // 20 秒后取消定时器
        //   setTimeout(() => {
        //     timeout.cancel();
        //   }, 20000);
          return {
            startTime:()=> example(timeout),
            cancelTime:()=> timeout.cancel(),
          }
    }

    
    /**
     * 打印日志
     */
    // static printLog = (...args: any[]) => {
    static printLog = (...args: any[]) => {
        if (!App.SHOW_LOG) {
            return
        }

        console.log(args)

        //               ,,))))))));,
		//            __)))))))))))))),
		// \|/       -\(((((''''((((((((.
		// -*-==//////((''  .     `)))))),
		// /|\      ))| o    ;-.    '(((((                                  ,(,
		//          ( `|    /  )    ;))))'                               ,_))^;(~
		//             |   |   |   ,))((((_     _____------~~~-.        %,;(;(>';'~
		//             o_);   ;    )))(((` ~---~  `::           \      %%~~)(v;(`('~
		//                   ;    ''''````         `:       `:::|\,__,%%    );`'; ~
		//                  |   _     cjw        )     /      `:|`----'     `-'
		//            ______/\/~    |                 /        /
		//          /~;;.____/;;'  /          ___--,-(   `;;;/
		//         / //  _;______;'------~~~~~    /;;/\    /
		//        //  | |                        / ;   \;;,\
		//       (<_  | ;                      /',/-----'  _>
		//        \_| ||_                     //~;~~~~~~~~~
		//            `\_|                   (,~~
		//                                    \~\
		//                                     ~~
    }

    /**
     * 文本居中对齐（X锚点为0）
     * @param nodes 文本数组
     * @param relValue X起始点坐标
     * @param offset 间距
     * @param origin 从起始点开始适配
     */
    static adaptiveLabel = (nodes:Array<Node>, relValue = 0, offset = 2, origin=null)=> {
        if (nodes == null) {return}

        // relValue = relValue || 0
        // offset = offset || 2

        for (let i = 0; i < nodes.length; i++) {
            nodes[i].setScale(0,0,0)
        }
        setTimeout(() => {
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].setScale(1,1,1)
            }
            
            let pw = 0
            for (let i = 0; i < nodes.length; i++) {
                if (i == 0) {
                    pw = pw + nodes[i].getComponent(UITransform).contentSize.width 
                } 
                else {
                    pw = pw + nodes[i].getComponent(UITransform).contentSize.width + offset
                }
            }
    
            let px = origin || -pw/2
            let allX = 0
            for (let i = 0; i < nodes.length; i++) {
                let posX = relValue + px + allX + nodes[i].getComponent(UITransform).anchorX * nodes[i].getComponent(UITransform).contentSize.width + i*offset
                nodes[i].setPosition(posX, nodes[i].position.y)
                allX = allX + nodes[i].getComponent(UITransform).contentSize.width
            }
        }, 100)
    }

    /**
     * 文本右往左对其 （X锚点为1）
     * @param nodes 文本数组
     * @param relValue X起始点坐标
     * @param offset 间距
     */
    static adaptiveLabel2 = (nodes:Array<Node>, relValue=0, offset=2) => {
        if (nodes == null) {return}
        
        for (let i = 0; i < nodes.length; i++) {
            nodes[i].setScale(0,0,0)
        }
        setTimeout(() => {
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].setScale(1,1,1)
            }
            let allX = 0
            for (let i = nodes.length; 1; -1) {
                nodes[i].setPosition(relValue - allX, nodes[i].position.y)
                allX = allX + nodes[i].getComponent(UITransform).contentSize.width + offset
            }
        }, 100)
    }

    /**
     * 文本左往右对其 （X锚点为0）
     * @param nodes 文本数组
     * @param relValue X起始点坐标
     * @param offset 间距
     */
    static adaptiveLabel3 = (nodes:Array<Node>, relValue=0, offset=2) => {
        if (nodes == null) {return}
        
        for (let i = 0; i < nodes.length; i++) {
            nodes[i].setScale(0,0,0)
        }
        setTimeout(() => {
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].setScale(1,1,1)
            }

            let allX = 0
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].setPosition(relValue + allX, nodes[i].position.y)
                allX = allX + nodes[i].getComponent(UITransform).contentSize.width + offset
            }
        }, 100)
    }

    // 屏幕全屏设置
    static setScreen = (nodeObj:Node) => {
        let size = view.getVisibleSize()

        let touch = nodeObj.getChildByName("SpriteSplash-001")
        if (touch) {
            touch.getComponent(UITransform).anchorPoint = new Vec2(0.5,0.5)
            touch.setPosition(0,0)
            touch.getComponent(UITransform).setContentSize(size)
        }

        let basebg = nodeObj.getChildByName("basebg")
        if (basebg) {
            basebg.getComponent(UITransform).anchorPoint = new Vec2(0.5,0.5)
            basebg.setPosition(0,0)
            basebg.getComponent(UITransform).setContentSize(size)
        }
    }
        

    // 获取屏幕大小差值
    static getGapSize = () =>{
        let vs = view.getVisibleSize()
        let ds = view.getDesignResolutionSize()
        // return {width : Math.abs(vs.width - ds.width)/2, height : Math.abs(vs.height - ds.height)/2}
        return {width : (vs.width - ds.width)/2, height : (vs.height - ds.height)/2}
    }
    
    /**
     * 文本逐字显示
     * @param parent 父节点
     * @param laber 文本组件
     * @param str 字符串
     * @param time 间隔时间
     * @param cb 回调
     */
    static showLaberOnebyone = (para) =>{
        var parent = para.parent
        var laber = para.laber
        var str = para.str || ""
        var time = para.time || 0.1
        var cb = para.cb
        if (parent==null || laber==null || str=="") return

        var curStr = ""
        var arr = str.split('')
        var step = 0

        
        parent.loboFun = function(){
            curStr += arr[step]
            if (laber) laber.string = curStr

            if (step<arr.length-6) {
                App.Audio.playOneShot("digit")
            }
            else if (step<arr.length-2) {
                App.Audio.playOneShot("digit",true,0.6)
            }
            else {
                App.Audio.playOneShot("digit",true,0.2)
            }
            
            if (++step>=arr.length) {
                parent.unschedule(parent.loboFun)
                cb && cb()
            }
        }
        parent.schedule(parent.loboFun, time, macro.REPEAT_FOREVER)
    }

    // 设置位置
    static setPos = (node:Node, value:any,isUp:boolean=false) =>{
        if (node == null) {
            return
        }

        var pos = node.getPosition()
        var gapw = Math.abs(this.getGapSize().width)
        var gaph = Math.abs(this.getGapSize().height)
        let vs = view.getVisibleSize()
        let ds = view.getDesignResolutionSize()
        if (vs.height>ds.height) {
            if (isUp){
                node.setPosition(pos.x + value[0] * gapw, pos.y + value[1] * gaph)
            }
            else{
                var aaa = pos.y + value[1] * -gaph
                node.setPosition(pos.x + value[0] * gapw, pos.y + value[1] * -gaph)
            }
        }
        else{
            if (isUp){
                node.setPosition(pos.x + value[0] * gapw, pos.y + value[1] * -gaph)
            }
            else{
                node.setPosition(pos.x + value[0] * gapw, pos.y + value[1] * gaph)
            }
        }

        /* var gapw = Math.abs(this.getGapSize().width)
        var gaph = Math.abs(this.getGapSize().height)
        var pos = node.getPosition()
        node.setPosition(pos.x + value[0] * gapw, pos.y + value[1] *  gaph) */
    }

    /**   
     * 设置大小
    */
    static setSize = (node, ptype, offset) =>{
        if (node == null) {
            return
        }
    
        var scaleW = 1.0
        var scaleH = 1.0
        let vs = view.getVisibleSize()
        if (ptype == null) {    // 适配宽、高
            scaleW = vs.width / node.getComponent(UITransform).contentSize.width
            scaleH = vs.height / node.getComponent(UITransform).contentSize.height
            node.getComponent(UITransform).setContentSize(vs)
        }
        else if (ptype == 1) { // 适配宽度
            scaleW = vs.width / node.getComponent(UITransform).contentSize.width
            node.getComponent(UITransform).setContentSize(vs.width, node.getComponent(UITransform).contentSize.height)
        }
        else if (ptype == 2) { // 适配高度
            scaleH = vs.height / node.getComponent(UITransform).contentSize.height
            node.getComponent(UITransform).setContentSize(node.getComponent(UITransform).contentSize.width, vs.height)
        }
        else if (ptype == 3) { // 根据offset增加间隔宽高
            offset = offset || [0, 0]
            node.getComponent(UITransform).setContentSize(
                node.getComponent(UITransform).contentSize.width + offset[0]*this.getGapSize().width,
                node.getComponent(UITransform).contentSize.height + offset[1]*this.getGapSize().height
            )
        }
        else if (ptype == 4) { // 缩放高度
            var scale = vs.height / node.getComponent(UITransform).contentSize.height
            var size = node.getComponent(UITransform).contentSize
            size = new math.Size(size.width * scale, size.height * scale)
            if (size.width < vs.width) {
                size.width = vs.width
            }
            node.getComponent(UITransform).setContentSize(size)
        }
        else if (ptype == 5) { // 缩放宽度
            scaleW = vs.width / node.getComponent(UITransform).contentSize.width
            var size = node.getComponent(UITransform).contentSize
            scaleH = scaleW
            var startH = size.height
            size = new math.Size(size.width*scaleW, size.height*scaleH)
            if (size.height < vs.height) {
                size.height = vs.height
                scaleH = vs.height/startH
            }
            node.getComponent(UITransform).setContentSize(size)
        }
    
        return {scaleW,scaleH}
    }

    /**   
     * 设置界面出场动画
     * @param ptype [1]从下往上滑出 [2]从上往下滑出 [3]从左往右滑出 [4]从右往左滑出 [5]透明度慢慢显示出来
    */
    static nodeActive(node:Node, ptype:number=1, time:number=0.2) {
        if (node == null) {
            return
        }
        var vs = view.getVisibleSize()
        
        if (ptype==1) {
            node.setPosition(node.getPosition().x, -vs.height/2-node.getComponent(UITransform).contentSize.height/2)
            tween(node)
            .to(time, { position: new Vec3(node.getPosition().x, 0, 0)}).start()
        }
        else if (ptype==2) {
            node.setPosition(node.getPosition().x, vs.height/2+node.getComponent(UITransform).contentSize.height/2)
            tween(node)
            .to(time, { position: new Vec3(node.getPosition().x, 0, 0)}).start()
        }
        else if (ptype==3) {
            node.setPosition(-vs.width/2-node.getComponent(UITransform).contentSize.width/2, node.getPosition().y)
            tween(node)
            .to(time, { position: new Vec3(0, node.getPosition().y, 0)}).start()
        }
        else if (ptype==4) {
            node.setPosition(vs.width/2+node.getComponent(UITransform).contentSize.width/2, node.getPosition().y)
            tween(node)
            .to(time, { position: new Vec3(0, node.getPosition().y, 0)}).start()
        }
        else if (ptype==5) {
            node.addComponent(UIOpacity);
            let maxScale = JSON.parse(JSON.stringify(node.scale))
            node.setScale(0,0);
            node.getComponent(UIOpacity).opacity = 0;
            tween(node)
                .to(time, { scale: maxScale }).start()
            tween(node.getComponent(UIOpacity))
                .to(time, { opacity: 255 }).start()
        }
    }

    /**
     * 将时间戳格式化为yyyy-MM-dd hh:mm:ss
     * @param val 剩余时间戳
     * @param type 显示类型 [0]显示时分秒  [1]显示分秒 [2]显示秒
     * @returns 
     */
    static formatTime(time: number,type=0): string {
        if (time == null || time < 0 ) {
            return "00:00:00"
        }

        var hour = 0
        var minute = 0
        var second = 0

        second = time % 60
        time = Math.floor(time / 60)
        minute = time % 60
        time = Math.floor(time / 60)
        hour = time % 24

        var hourStr = hour>=10?"" + hour : "0" + hour
        var minuteStr = minute>=10?"" + minute : "0" + minute
        var secondStr = second>=10?"" + second : "0" + second
        var secondStr_one = second

        if (type==0) {
            return hourStr + ":" + minuteStr + ":" + secondStr
        }   
        else if (type==1) {
            return minuteStr + ":" + secondStr
        }   
        else if (type==2) {
            return secondStr_one + ""
        }
    }

    /**
     * 获取时间戳数据
     * @param time 时间戳
     */
    static getTimeData(time : number) {

        // 返回前置0
        let getStr = (value)=>{
            return ("0" + value).slice(-2)
        }

        let date = new Date(time);
        let year = date.getFullYear();
        let month = getStr(date.getMonth() + 1);
        let day = getStr(date.getDate());
        let hour =  getStr(date.getHours());
        let minute = getStr(date.getMinutes());
        let second = getStr(date.getSeconds());
        return {
            year : year,
            month : month,
            day : day,
            hour : hour,
            min : minute,
            second : second
        }
    }

    /**   
     * 时间倒计时
     * @param node 文本控件
     * @param time 时间戳
     * @param cb 回调
    */
    static timeCountdown(laberText:Label, laberTextShadow:Label, time, cb) {
        if (laberText==null) {
            return
        }

        if (time<=0) {
            laberText.string = "00:00:00"
            if (laberTextShadow) laberTextShadow.string = "00:00:00"
            cb && cb()
            return
        }

        let stopDJS = function() {
            // 停止倒计时
            Tween.stopAllByTarget(laberText.node);
        }

        stopDJS()
        laberText.string = UtilTool.formatTime(time)
        if (laberTextShadow) laberTextShadow.string = laberText.string
        let embedTween = tween(laberText.node)
            .delay(1.0)
            .call(() => {
                time = time - 1
                
                if (time<0) {
                    stopDJS()
                    cb && cb()
                    return
                }
                laberText.string = UtilTool.formatTime(time)
                if (laberTextShadow) laberTextShadow.string = laberText.string
            })
        tween(laberText.node)
            .repeatForever(embedTween)
            .start()
    }

    /**   
     * 时间倒计时(显示xxs)
     * @param node 文本控件
     * @param time 剩余时间
     * @param cb 回调
    */
    static timeCountdown2(laberText:Label, time, cb) {
        if (laberText==null) {
            return
        }

        if (time<=0) {
            laberText.string = App.language.getLanguageLab(21) +  " 0s"
            cb && cb()
            return
        }

        let stopDJS = function() {
            // 停止倒计时
            Tween.stopAllByTarget(laberText.node);
        }

        stopDJS()
        laberText.string = App.language.getLanguageLab(21) + " " + time + "s"
        let embedTween = tween(laberText.node)
            .delay(1.0)
            .call(() => {
                time = time - 1
                
                if (time<0) {
                    stopDJS()
                    cb && cb()
                    return
                }
                laberText.string = App.language.getLanguageLab(21) + " " + time + "s"
            })
        tween(laberText.node)
            .repeatForever(embedTween)
            .start()
    }

    /**   
     * 时间倒计时3 （每秒都会掉回调 1:每一秒的回调，2:倒计时结束回调）
     * @param node 文本控件
     * @param time 时间戳
     * @param cb 回调
     * @param type 显示类型 [0]显示时分秒  [1]显示分秒 [2]显示秒
    */
    static timeCountdown3(laberText:Label, time, cb, type=1) {
        if (laberText==null) {
            return
        }

        if (time<=0) {
            if (type==0) {
                laberText.string = "00:00:00"
            }else if (type==1) {
                laberText.string = "00:00"
            }else if (type==2) {
                laberText.string = "0"
            }
            
            cb && cb(2)
            return
        }

        let stopDJS = function() {
            // 停止倒计时
            Tween.stopAllByTarget(laberText.node);
        }

        stopDJS()
        laberText.string = UtilTool.formatTime(time,type)
        let embedTween = tween(laberText.node)
            .delay(1.0)
            .call(() => {
                time = time - 1
                cb && cb(1,time)

                if (time<0) {
                    stopDJS()
                    cb && cb(2)
                    return
                }
                laberText.string = UtilTool.formatTime(time,type)
            })
        tween(laberText.node)
            .repeatForever(embedTween)
            .start()
    }

    /**   
     * 单位换算
     * @param value:       数值
     * @param para.sign:   标记 "万" || "亿"
     * @param para.digit:  保留小数点位数
     * @param para.type:   格式化法 [1]四舍五入 [2]向下取整 [3]向上取整
    */
    static formatValue(value:number, para){
        if (value == null) {
            return ""
        }

        para = para || {}
        para.digit = para.digit==null?1:para.digit
        para.type = para.type==null?1:para.type

        var yi = 1000000000 // [10亿] 单位：B
        var wan = 1000000   // [100万] 单位：M
        var qian = 1000     // [1千] 单位：K
        // var yiMark = yi/(10**para.digit)   
        // var waMark = wan/(10**para.digit)
        // var qiMark = qian/(10**para.digit)

        // 根据保留指定小数位数，获取小数点后面的值
        let getDigFunc = function(index) {
            let valueStr = value.toString()
            let xs = ""

            if (para.digit==0) {
                return xs
            }

            xs = "."
            for (let i = 0; i < valueStr.length; i++) {
                let element = valueStr[i]
                if (i>=valueStr.length-index) {
                    xs = xs + element
                    para.digit = para.digit - 1
                }
                if (para.digit==0) {
                    break
                }
            }
            return xs
        }


        // [10亿]
        if (value >= yi && (para.sign == App.language.getLanguageLab(45) || para.sign == null)) { 
            if (para.type==1) {
                return (value/yi).toFixed(para.digit) + App.language.getLanguageLab(45)
            }
            else if (para.type==2){
                let gid = getDigFunc(9)
                return Math.floor(value/yi) + gid +  App.language.getLanguageLab(45)
                // return Math.floor(value/yi) + '.' + Math.floor(value%yi/yiMark) +  App.language.getLanguageLab(45)
            }
            else if (para.type==3){
                value = value / yi
                return Math.ceil(value * (10**para.digit)) / (10**para.digit) + App.language.getLanguageLab(45)
            }
        }

        // [100万]
        if (value >= wan && (para.sign == App.language.getLanguageLab(44) || para.sign == null)){
            if (para.type==1) {
                return (value/wan).toFixed(para.digit) + App.language.getLanguageLab(44)
            }
            else if (para.type==2) {
                let gid = getDigFunc(6)
                return Math.floor(value/wan) + gid + App.language.getLanguageLab(44)
                // return Math.floor(value/wan) + '.' + Math.floor(value%wan/waMark) + App.language.getLanguageLab(4)
            }
            else if (para.type==3){
                value = value / wan
                return Math.ceil(value * (10**para.digit)) / (10**para.digit) + App.language.getLanguageLab(44)
            }
        }

        // [1千] 暂时不需要这个单位
        /* if (value >= qian && (para.sign == App.language.getLanguageLab(157) || para.sign == null)){
            if (para.type==1) {
                return (value/qian).toFixed(para.digit) + App.language.getLanguageLab(157)
            }
            else if (para.type==2) {
                let gid = getDigFunc(3)
                return Math.floor(value/qian) + gid + App.language.getLanguageLab(157)
                // return Math.floor(value/qian) + '.' + Math.floor(value%qian/qiMark) + App.language.getLanguageLab(157)
            }
            else if (para.type==3){
                value = value / qian
                return Math.ceil(value * (10**para.digit)) / (10**para.digit) + App.language.getLanguageLab(157)
            }
        } */

        // 没有单位
        return value.toString()
    }

    /**   
     * 格式化钱包地址
     * @param str:          钱包完整地址
     * @param preLength:    前面截取的长度
     * @param afterLength:  后面截取的长度
    */
    static getShortWalletAddr(str:string, preLength:number = 4, afterLength:number = 4):string {
        var strLength = str.length
        if (strLength > preLength + afterLength + 2){
            return str.substring(0,preLength) + "..." + str.substring(strLength-afterLength,strLength)
        }
        else{
            return str
        }
    }

    /**   
     * 获取昵称或者钱包地址
     * @param nickname:        昵称
     * @param address:         地址
     * @param addressLen:      地址保留显示的长度
     * @param nickLen:         昵称保留显示的长度
    */
    static formatNickOrAddress(data) {
        let nick = data.nickname
        let address = data.address

        let addressLen = data.addressLen || 4
        let nickLen = data.nickLen || 10
        
        if (nick==null || nick=="") {
            return UtilTool.getShortWalletAddr(address, addressLen, addressLen)
        } else {
            return UtilTool.getFormatNickname(nick, nickLen)
        }
    }

    /**   
     * 格式化昵称 (中文汉字算2个字节,其它的字符都算一个字节)
     * @param nickStr:      昵称
     * @param len:          保留字节长度
    */
    static getFormatNickname(nickStr:string, len:number = 10) {
        let charLen = 0
        let nickname = ""
        for (let i = 0; i < nickStr.length; i++) {
             // let char = nickStr.charCodeAt(i)

            let str = nickStr[i]
            var reg = new RegExp("[\\u4E00-\\u9FFF]+","g");
            let isC = reg.test(str)
            if (isC) { // 中文
                charLen = charLen + 2
            }else{
                charLen++
            }

            if (charLen>len) {
                nickname = nickname + "..."
                break
            }

            nickname = nickname + str
        }

        return nickname
    }

    // 获取模型大小
    static getModelSize(model : Node) {
        let struct : Mesh.IStruct = model.getComponent(ModelComponent).mesh.struct;
        let worldScale = model.worldScale;
        let scalex = worldScale.x * Math.abs(struct.maxPosition.x - struct.minPosition.x)
        let scaley = worldScale.y * Math.abs(struct.maxPosition.y - struct.minPosition.y)
        let scalez = worldScale.z * Math.abs(struct.maxPosition.z - struct.minPosition.z)
        
        return new Vec3(scalex, scaley, scalez)
    }
    
    // 向量相减
    static subVector2(v1:Vec2, v2:Vec2) : Vec2 {
        return v1.clone().subtract(v2)
    }
    
    // 向量相加
    static addVector2(v1:Vec2, v2:Vec2) : Vec2 {
        return v1.clone().add(v2)
    }

    /**   
     * 过滤掉非数字字符
     * @param str 字符串
    */
    static getNumberStr(str:string):string {
        var numberStr = ""
        for (let index = 0; index < str.length; index++) {
            let curChar = str[index]
            let char = str.charCodeAt(index)
            if (char>=48 && char<=57) { // 数字的ASCII码
                numberStr = numberStr + curChar
            }
        }
        
        return numberStr
    }

    /**   
     * 获取居中显示的x轴位置
     * @param count 节点个数
     * @param size 节点大小
     * @param index 节点下标 (从0开始)
    */
    static getSortPosX(count,size,index) {
        let startX = -count*size/2 + size/2 + index*size
        return startX
    }

    /**
     * 显示文本等待动画...
     * @param textLabel 文本组件
     * @param text 文本
     * @param stepTime 间隔1秒
     */
    static showTextWaitAction(textLabel : Label, text : string, stepTime=1) {

        textLabel.string = text + "."

        let count = 1
        let repeatTween = tween(textLabel.node).delay(stepTime).call(() => {
            count++
            if(count > 3) {
                count = 1
            }
            textLabel.string = text + ".".repeat(count)
        })
        tween(textLabel.node).repeatForever(repeatTween).start()
    }

    // 显示呼吸动画
    static showBreatheAction(target : Node, isStart=true) {
        let tweenScale = tween(target)
        .to(0.4, {scale: new Vec3(1.05, 1.05, 1)})
        .to(0.4, {scale: new Vec3(0.95, 0.95, 1)})
        .to(0.4, {scale: new Vec3(1, 1, 1)})
        
        let tweenAction = tween(target).repeatForever(tweenScale)
        if(isStart) {
            tweenAction.start()
        }
        
        return tweenAction
    }

    /**
     * 获取房间号字符串
     * 小于三位的就自动补两个0，大于三位的就不用补0了
     * 如 000001 > 001; 001234 > 1234
     */ 
    static getRoomNumStr(roomId) : string {
        let roomIdStr = parseInt(roomId).toString()
        if(roomIdStr.length < 3) {
            roomIdStr = "0".repeat(3 - roomIdStr.length) + roomIdStr
        }
        return roomIdStr
    }

    // 图片置灰
    static imgToGray(imgNode : Node) {
        let sp = imgNode.getComponent(Sprite)
        if(sp) {
            sp.grayscale = true
        } else {
            console.error("image to gray not has sprite commponet")
        }
    }

    /**
     * 获取url参数值, 
     * @param key, href为空时，默认取当前页面url
     * @returns 
     */
    static getUrlParamValue(key:string, href:string=null) {
        href = href || window.location.href
        let url : URL = new URL(href)
        let value = url.searchParams.get(key)
        return value
    }

    // 显示渐入动画
    static showFadeInAction(node : Node) {
        tween()
        .target(node)
        .to(0.07,{ scale: new Vec3(1.2, 1.2, 1) })
        .to(0.1,{ scale: new Vec3(0.6, 0.6, 1) })
        .to(0.1,{ scale: new Vec3(1.1, 1.1, 1) })
        .to(0.05,{ scale: new Vec3(0.8, 0.8, 1) })
        .to(0.05,{ scale: new Vec3(1.0, 1.0, 1) })
        .to(0.05,{ scale: new Vec3(0.9, 0.9, 1) })
        .to(0.05,{ scale: new Vec3(1.0, 1.0, 1) })
        .start()
        
        tween(node.getComponent(UIOpacity))
            .delay(1.4)
            .to(0.4, { opacity: 0 }).start()
    }

    /**
     * 获取省略字符串
     * @param text 文本
     * @param totalLen 总长度（包含...）
     */
    static getIngoreText(text : string, totalLen : number) {

        if(text.length <= 3) {
            return text
        }

        if(text.length <= totalLen) {
            return text
        }

        return text.substring(0, totalLen-3) + "..."
    }

    /**
     * 格式化保留指定小数位数
     * @param value 数字
     * @param count 保留的小数位数
     */
    static formatDigits(value:number, count:number = 6):number {
        let strV = value.toString()

        let dig = false
        let digCount = 0
        for (let i = 0; i < strV.length; i++) {
            let char = strV[i]

            if (dig) {
                digCount++
            }

            if (char=="."){
                dig = true
            }

            if (digCount>count){
                break
            }
        } 

        if (digCount>count){
            return parseFloat(value.toFixed(count))
        }
        else{
            return value
        }

    }

    // UI坐标转本地坐标
    static UIPosToLocalPos(uiPos) {
        let size = view.getVisibleSize()
        return new Vec3(uiPos.x - size.x/2, uiPos.y - size.y/2, 0)
    }

    // 本地坐标转UI坐标
    static LocalPosToUIPos(LocalPos) {
        let size = view.getVisibleSize()
        return new Vec3(LocalPos.x + size.x/2, LocalPos.y + size.y/2, 0)
    }

}